package edu.gatech.gro.http;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONArray;
import org.json.JSONObject;

import android.net.http.AndroidHttpClient;
import android.os.Looper;
import android.util.Log;
import edu.gatech.gro.model.ObjectResponse;
import edu.gatech.gro.model.parser.JsonHandler;

public class HttpRequest implements Runnable {

	private static final String TAG = "HTTP_REQUEST";
	private static final String USER_AGENT = "GroceryList211";

	public static final int GET = 0;
	public static final int POST = 1;
	public static final int PUT = 2;
	public static final int DELETE = 3;

	private static List<NameValuePair> mCookies = new ArrayList<NameValuePair>();

	private final int mType;
	private final String mUrl;
	private final HashMap<String, String> mGetParameters;
	private final CommonResponseHandler mJsonHandler;

	private String mHttpUrl;
	private HttpEntity mContentEntity;
	private boolean sendJson = false;
	private IResponseAdapter mListener;

	public HttpRequest(int reqType, String url, JsonHandler jsonHandler) {
		mType = reqType;
		mUrl = url;
		mGetParameters = new HashMap<String, String>();
		mJsonHandler = new CommonResponseHandler(jsonHandler);
	}

	public void addGetParameter(String name, String value) {
		mGetParameters.put(name, value);
	}

	public void setContent(JSONObject json) {
		mContentEntity = null;
		sendJson = false;
		try {
			mContentEntity = new StringEntity(json.toString());
			sendJson = true;
		} catch (UnsupportedEncodingException e) {
			Log.d(TAG, e.getMessage());
		}
	}

	public void setContent(JSONArray json) {
		mContentEntity = null;
		sendJson = false;
		try {
			mContentEntity = new StringEntity(json.toString());
			sendJson = true;
		} catch (UnsupportedEncodingException e) {
			Log.d(TAG, e.getMessage());
		}
	}

	public void setContent(List<NameValuePair> params) {
		mContentEntity = null;
		sendJson = false;
		try {
			if (params != null) {
				mContentEntity = new UrlEncodedFormEntity(params);
			}
		} catch (UnsupportedEncodingException e) {
			Log.d(TAG, e.getMessage());
		}
	}

	public void setResponseListener(IResponseAdapter listener) {
		mListener = listener;
	}

	@Override
	public void run() {
		prepareUrl();

		HttpUriRequest http = null;
		switch (mType) {
			case POST:
				http = post();
				break;
			case PUT:
				http = put();
				break;
			case DELETE:
				http = delete();
				break;
			case GET:
			default:
				http = get();
				break;
		}
		if (http != null) {

			AndroidHttpClient httpclient = null;
			try {
				httpclient = AndroidHttpClient.newInstance(USER_AGENT);

				setHeaders(http);

				http.setHeader("Accept", "application/json");
				if (sendJson) {
					http.setHeader("Content-type", "application/json");
				}

				HttpResponse response = httpclient.execute(http);
				ObjectResponse res = mJsonHandler.handleResponse(response);
				saveCookies(response);

				if (mListener != null) {
					Looper.prepare();
					mListener.onResponse(res);
				}
			} catch (Exception e) {
				String msg = e != null && e.getMessage() != null ? e.getMessage() : "FATAL ERROR!";
				Log.e(TAG, msg);
			}
			finally {
				if (httpclient != null) {
					httpclient.close();
				}
			}
		}
	}

	private void prepareUrl() {
		mHttpUrl = mUrl;
		if (!mHttpUrl.contains("?") && mGetParameters.size() > 0) {
			mHttpUrl += "?";
		}
		for (String key : mGetParameters.keySet()) {
			mHttpUrl += key + "=" + mGetParameters.get(key);
		}
	}

	private HttpUriRequest get() {
		return new HttpGet(mUrl);
	}

	private HttpUriRequest post() {
		HttpPost http = new HttpPost(mHttpUrl);
		if (mContentEntity != null) {
			http.setEntity(mContentEntity);
		}
		return http;
	}

	private HttpUriRequest put() {
		HttpPut http = new HttpPut(mHttpUrl);
		if (mContentEntity != null) {
			http.setEntity(mContentEntity);
		}
		return http;
	}

	private HttpUriRequest delete() {
		return new HttpDelete(mHttpUrl);
	}

	private synchronized void saveCookies(HttpResponse r) {
		Header[] headers = r.getHeaders("Set-Cookie");
		for (Header h : headers) {
			String[] cc = h.getValue().split(";");
			for (String c : cc) {
				String[] nv = c.split("=");
				if (nv.length == 2) {
					updateCookie(nv[0], nv[1]);
				}
			}
		}
	}

	private void updateCookie(String name, String value) {
		NameValuePair co = getCookie(name);
		if (co != null) {
			removeCookie(name);
		}
		mCookies.add(new BasicNameValuePair(name, value));
	}

	private boolean removeCookie(String name) {
		String n = name.toLowerCase();
		for (int i = 0; i < mCookies.size(); i++) {
			if (mCookies.get(i).getName().toLowerCase().equals(n)) {
				mCookies.remove(i);
				return true;
			}
		}
		return false;
	}

	private NameValuePair getCookie(String name) {
		String n = name.toLowerCase();
		for (NameValuePair c : mCookies)
			if (c.getName().toLowerCase().equals(n))
				return c;
		return null;
	}

	private synchronized void setHeaders(org.apache.http.HttpRequest r) {
		StringBuilder sb = new StringBuilder();
		for (NameValuePair c : mCookies) {
			if (sb.length() > 0)
				sb.append("; ");
			sb.append(c.getName() + "=" + c.getValue());
		}
		if (sb.length() > 0) {
			// Log.d(TAG, "Cookie:" + sb.toString());
			r.addHeader("Cookie", sb.toString());
		}
	}
}
